#include <boost/foreach.hpp>

#include "tinyxml.h"
#include "stdafx.h"
#include "collada.h"

#ifdef _DEBUG
#define DEBUG_NEW new( _NORMAL_BLOCK, __FILE__, __LINE__ )
#define new DEBUG_NEW
#endif

using namespace Collada;

IMPLEMENT_RTTI( Collada::Mesh, Collada::Element )
IMPLEMENT_ELEMENT( Collada::Mesh )

Mesh::Mesh( const std::string& id, const std::string& name )
:	Element( id, name )
{
	CHILD_ELEMENT_BEGIN()
		CHILD_ELEMENT( "source", Collada::Source )
		CHILD_ELEMENT( "vertices", Collada::Empty )
		CHILD_ELEMENT( "polygons", Collada::Empty )
		CHILD_ELEMENT( "extra", Collada::Empty )
	CHILD_ELEMENT_END()
}

Mesh::~Mesh()
{
	std::vector< Primitive* >::iterator it = _primitives.begin();
	for( ; it != _primitives.end(); ++it )
	{
		if( ( *it )->indices )
		{
			delete ( *it )->indices;
		}
		delete ( *it );
	}
	_primitives.clear();

	std::vector< Source* >::iterator iter = _sourceList.begin();
	for( ; iter != _sourceList.end(); ++iter )
	{
		if( *iter )
		{
			delete ( *iter );
			*iter = NULL;
		}
	}
}

bool Mesh::AddSource( Source* pSource )
{
	if( pSource == NULL )
	{
		return false;
	}

	_sourceList.push_back( pSource );
	return true;
}

void Mesh::AllChildElementsLoaded( Element* pParent )
{
	for( size_t i = 0; i < _primitiveInfos.size(); ++i )
	{
		PrimitiveInfo& primInfo = _primitiveInfos[i];
		Primitive* pPrim = new Primitive;
		
		// Set primitive type
		pPrim->type = StrToPrimitiveType( primInfo.typeStr );
		
		// Set vertex element
		int offset = 0;
		for( size_t j = 0; j < primInfo.accessorList.size(); ++j )
		{
			Semantic semantic = primInfo.accessorList[j].first;
			
			ID srcId = primInfo.accessorList[j].second;
			Source* pSource = GetSource( srcId );

			if( pSource == NULL )
			{
				assert( false && "Wrong Format!" );
			}

			TypeAndFormat tnf	= pSource->GetTypeAndFormat();
			Format format		= tnf & COLLADA_FORMAT_MASK;
			Type type			= tnf & COLLADA_TYPE_MASK;

			offset += SizeOfType( type ); 

			pPrim->vertexElements.push_back( type | semantic );
			pPrim->buffers.push_back( pSource->GetArray() );
		}

		// Set index buffer
		int bufferSize = primInfo.indexCount * offset;
		short* pIndex = new short[ bufferSize ];

		char* pStream = const_cast< char* >( primInfo.indexStream.c_str() ) ;
		for( int i = 0; i < bufferSize; ++i )
		{
			sscanf_s( pStream, "%hd", &pIndex[i] );
			strtok_s( pStream, " ", &pStream );
		}

		pPrim->indices = ( void* )pIndex;
		pPrim->indexCount = bufferSize;

		_primitives.push_back( pPrim );
	}

	_primitiveInfos.clear();
	_vertices.clear();

	( ( Geometry* )pParent )->AddGeometricData( this );
}

Source* Mesh::GetSource( ID sourceId )
{
	for( size_t i = 0; i < _sourceList.size(); ++i )
	{
		if( sourceId == _sourceList[i]->GetSourceID() )
		{
			return _sourceList[i];
		}
	}

	return NULL;
}

void Mesh::Initialize( Element* pParent, const void* desc )
{
	TiXmlElement* pElem = ( TiXmlElement* )desc;
	TiXmlElement* pChild = pElem->FirstChildElement( "vertices" );
	
	// vertices
	if( pChild != NULL )
	{
		TiXmlElement* pInput = pChild->FirstChildElement( "input" );
		const char* semantic	= pInput->Attribute( "semantic" );
		const char* source		= pInput->Attribute( "source" );

		ID srcId = Fundamental::JenkinsPHF( ( source[0] == '#' )? &source[1] : &source[0] );
		_vertices.push_back( Accessor( StrToSemantic( semantic ), srcId ) );
	}

	// primitives
	pChild = pChild->NextSiblingElement();
	for( ; pChild; pChild = pChild->NextSiblingElement() )
	{
		if( std::string( pChild->Value() ) == "extra" )
		{
			break;
		}

		PrimitiveInfo info;
		info.typeStr = pChild->Value();

		TiXmlElement* pInput = pChild->FirstChildElement( "input" );
		for( ; pInput; pInput = pInput->NextSiblingElement() )
		{
			if( std::string( pInput->Value() ) == "p" ||
				std::string( pInput->Value() ) == "ph" )
			{
				break;
			}

			if( std::string( pInput->Attribute( "semantic" ) ) == "VERTEX" )
			{
				for( size_t i = 0; i < _vertices.size(); ++i )
				{
					info.accessorList.push_back( _vertices[i] );
				}
				continue;
			}

			const char* semantic	= pInput->Attribute( "semantic" );
			const char* source		= pInput->Attribute( "source" );

			ID srcId = Fundamental::JenkinsPHF( ( source[0] == '#' )? &source[1] : &source[0] );
			info.accessorList.push_back( Accessor( StrToSemantic( semantic ), srcId ) );

		}

		for( ; pInput; pInput = pInput->NextSiblingElement() )
		{
			info.indexStream.append( pInput->GetText() );
			info.indexStream.append( " " );
		}

		info.indexCount = atoi( pChild->Attribute( "count" ) );
		_primitiveInfos.push_back( info );
	}

	// extra
}

bool Mesh::IsValid( const void* desc )
{
	TiXmlElement* pElem = ( TiXmlElement* )desc;
	TiXmlElement* pChild = pElem->FirstChildElement( "source" );
	if( pChild == NULL )
	{
		return false;
	}

	pChild = pElem->FirstChildElement( "vertices" );
	if( pChild == NULL )
	{
		return false;
	}
	return true;
}

void Mesh::SetBuffer( Primitive& prim, const std::string& stream, int bufferSize )
{
}